﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using Infrastructure.ObjectModel;
using Microsoft.Xna.Framework;
using Infrastructure.ServiceInterfaces;

namespace Infrastructure.Managers
{
    public class CollisionDetector : GameService, ICollisionDetector
    {
        private Dictionary<ObjectPair, EventHandler<SpriteEventArgs>> m_ChecksDictionary;
        
        public CollisionDetector(Game i_game)
            : base(i_game)
        {
            m_ChecksDictionary = new Dictionary<ObjectPair, EventHandler<SpriteEventArgs>>();
        }

        protected override void RegisterAsService()
        {
            Game.Services.AddService(typeof(ICollisionDetector), this);
        }

        public bool CheckCollision(Sprite i_Sprite1, Sprite i_Sprite2)
        {
            Rectangle rect1 = new Rectangle(
                (int)i_Sprite1.Position.X, 
                (int)i_Sprite1.Position.Y, 
                i_Sprite1.Width, 
                i_Sprite1.Height);
            Rectangle rect2 = new Rectangle(
                (int)i_Sprite2.Position.X,
                (int)i_Sprite2.Position.Y,
                i_Sprite2.Width,
                i_Sprite2.Height);

            return rect1.Intersects(rect2) && i_Sprite2.Enabled && i_Sprite1.Enabled;
        }

        public Sprite CheckCollision(Sprite i_Sprite1, List<Sprite> i_SpriteList)
        {
            Rectangle rect1 = new Rectangle(
                (int)i_Sprite1.Position.X,
                (int)i_Sprite1.Position.Y,
                i_Sprite1.Width,
                i_Sprite1.Height);

            // Checking all sprites in list to check if they collied
            foreach (Sprite CurrSprite in i_SpriteList)
            {
                Rectangle rect2 = new Rectangle(
                (int)CurrSprite.Position.X,
                (int)CurrSprite.Position.Y,
                CurrSprite.Width,
                CurrSprite.Height);

                if (rect1.Intersects(rect2))
                {
                    return CurrSprite;
                }
            }

            return null;
        }

        public List<Sprite> CheckCollision(List<Sprite> i_SpriteList1, List<Sprite> i_SpriteList2)
        {
            // Checking all sprites in list to check if they collied
            foreach (Sprite CurrSprite in i_SpriteList1)
            {
                Sprite retVal = CheckCollision(CurrSprite, i_SpriteList2);
                if (retVal != null)
                {
                    List<Sprite> retList = new List<Sprite>();
                    retList.Add(retVal);
                    retList.Add(CurrSprite);
                    return retList;
                }
            }

            return null;
        }

        public void RegisterCollisionCheck(Sprite i_Sprite1, Sprite i_Sprite2, EventHandler<SpriteEventArgs> i_actionMethod)
        {
            m_ChecksDictionary.Add(new ObjectPair(i_Sprite1, i_Sprite2), i_actionMethod);
        }

        public void RegisterCollisionCheck(Sprite i_Sprite1, List<Sprite> i_SpriteList, EventHandler<SpriteEventArgs> i_actionMethod)
        {
            m_ChecksDictionary.Add(new ObjectPair(i_Sprite1, i_SpriteList), i_actionMethod);
        }

        public void RegisterCollisionCheck(List<Sprite> i_SpriteList1, List<Sprite> i_SpriteList2, EventHandler<SpriteEventArgs> i_actionMethod)
        {
            m_ChecksDictionary.Add(new ObjectPair(i_SpriteList1, i_SpriteList2), i_actionMethod);
        }

        public void UnRegisterCollisionCheck(Sprite i_Sprite1, Sprite i_Sprite2)
        {
            m_ChecksDictionary.Remove(new ObjectPair(i_Sprite1, i_Sprite2));
        }

        public void UnRegisterCollisionCheck(Sprite i_Sprite1, List<Sprite> i_SpriteList)
        {
            m_ChecksDictionary.Remove(new ObjectPair(i_Sprite1, i_SpriteList));
        }

        public void UnRegisterCollisionCheck(List<Sprite> i_SpriteList1, List<Sprite> i_SpriteList2)
        {
            m_ChecksDictionary.Remove(new ObjectPair(i_SpriteList1, i_SpriteList2));
        }

        /// <summary>
        /// Checks the registered collisions
        /// </summary>
        /// <param name="gameTime">gameTime</param>
        public override void Update(GameTime gameTime)
        {
            // Checking all registered sprites
            foreach (KeyValuePair<ObjectPair, EventHandler<SpriteEventArgs>> pair in m_ChecksDictionary)
            {
                Sprite firstSprite = pair.Key.First as Sprite;
                Sprite secondSprite = pair.Key.Second as Sprite;
                List<Sprite> spriteList1 = pair.Key.First as List<Sprite>;
                List<Sprite> spriteList2 = pair.Key.Second as List<Sprite>;

                // Check if this is sprite to sprite check or sprite to list check
                if (spriteList1 != null)
                {
                    List<Sprite> SpriteCollied = this.CheckCollision(spriteList1, spriteList2);
                    if (SpriteCollied != null)
                    {
                        pair.Value.Invoke(firstSprite, new SpriteEventArgs(SpriteCollied));
                    }
                }
                else if (secondSprite != null)
                {
                    if (this.CheckCollision(firstSprite, secondSprite))
                    {
                        pair.Value.Invoke(firstSprite, new SpriteEventArgs(secondSprite));
                    }
                }
                else
                {
                    Sprite SpriteCollied = this.CheckCollision(firstSprite, spriteList2);
                    if (SpriteCollied != null)
                    {
                        pair.Value.Invoke(firstSprite, new SpriteEventArgs(SpriteCollied));
                    }
                }
            }

            base.Update(gameTime);
        }
    }
}
